#include "rtc.h"
#include <rtthread.h>
#include <time.h>
#include <dfs_posix.h>
#include "stm32f4xx.h"

static struct rt_device rtc;

static RTC_HandleTypeDef RTC_Handler;  //RTC句柄

static rt_err_t rtc_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

//框架中传下来的参数args 是经过计算的秒
//所以读取时间 需要将当前时间格式转换成秒
//设置时间 需要将秒转换为当前时间格式
static rt_err_t rtc_control(rt_device_t dev, rt_uint8_t cmd, void *args)
{
    time_t *time;
    RTC_TimeTypeDef RTC_TimeStructure;
    RTC_DateTypeDef RTC_DateStructure;
    struct tm time_temp;
    struct tm *time_new;

    RT_ASSERT(dev != RT_NULL);

    switch (cmd)
    {
    case RT_DEVICE_CTRL_RTC_GET_TIME:
        time = (time_t *)args;

        /* Get the current Time */
        HAL_RTC_GetTime(&RTC_Handler, &RTC_TimeStructure, RTC_FORMAT_BIN);
        HAL_RTC_GetDate(&RTC_Handler, &RTC_DateStructure, RTC_FORMAT_BIN);
        /* Years since 1900 : 0-99 range */
        time_temp.tm_year = RTC_DateStructure.Year + 2000 - 1900;
        /* Months *since* january 0-11 : RTC_Month_Date_Definitions 1 - 12 */
        time_temp.tm_mon = RTC_DateStructure.Month - 1;
        /* Day of the month 1-31 : 1-31 range */
        time_temp.tm_mday = RTC_DateStructure.Date;
        /* Hours since midnight 0-23 : 0-23 range */
        time_temp.tm_hour = RTC_TimeStructure.Hours;
        /* Minutes 0-59 : the 0-59 range */
        time_temp.tm_min = RTC_TimeStructure.Minutes;
        /* Seconds 0-59 : the 0-59 range */
        time_temp.tm_sec = RTC_TimeStructure.Seconds;

        *time = mktime(&time_temp);
        break;
    case RT_DEVICE_CTRL_RTC_SET_TIME:
        time = (time_t *)args;
        time_new = localtime(time);

        /* 0-99 range              : Years since 1900 */
        RTC_DateStructure.Year = time_new->tm_year + 1900 - 2000;
        /* RTC_Month_Date_Definitions 1 - 12 : Months *since* january 0-11 */
        RTC_DateStructure.Month = time_new->tm_mon + 1;
        /* 1-31 range : Day of the month 1-31 */
        RTC_DateStructure.Date = time_new->tm_mday;
        /* 1 - 7 : Days since Sunday (0-6) */
        RTC_DateStructure.WeekDay = time_new->tm_wday + 1;
        /* 0-23 range : Hours since midnight 0-23 */
        RTC_TimeStructure.Hours = time_new->tm_hour;
        /* the 0-59 range : Minutes 0-59 */
        RTC_TimeStructure.Minutes = time_new->tm_min;
        /* the 0-59 range : Seconds 0-59 */
        RTC_TimeStructure.Seconds = time_new->tm_sec;

        /* Allow access to RTC */
        HAL_PWR_EnableBkUpAccess();

        if (HAL_RTC_SetTime(&RTC_Handler, &RTC_TimeStructure, RTC_FORMAT_BIN) != HAL_OK)
            goto write_error;

        if (HAL_RTC_SetDate(&RTC_Handler, &RTC_DateStructure, RTC_FORMAT_BIN) != HAL_OK)
            goto write_error;

        /*not Allow access to RTC */
        HAL_PWR_DisableBkUpAccess();
        break;
    default:
        break;
    }
    return RT_EOK;

write_error:
    /*not Allow access to RTC */
    HAL_PWR_DisableBkUpAccess();
    return RT_ERROR;
}

//RTC底层驱动，时钟配置
//此函数会被HAL_RTC_Init()调用
//hrtc:RTC句柄
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;

    __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟PWR
    HAL_PWR_EnableBkUpAccess();//取消备份区域写保护

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; //LSE配置
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;  //针对HSE才有pll分频为1Hz
    RCC_OscInitStruct.LSEState = RCC_LSE_ON;                //RTC使用LSE
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; //外设为RTC
    PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; //RTC时钟源为LSE
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);

    __HAL_RCC_RTC_ENABLE();//RTC时钟使能
}

//RTC初始化设置
static rt_err_t RTC_Configuration(void)
{
    RTC_TimeTypeDef RTC_TimeStructure;
    RTC_DateTypeDef RTC_DateStructure;

    RTC_Handler.Instance = RTC; //安装RTC寄存器地址
    RTC_Handler.Init.HourFormat = RTC_HOURFORMAT_24; //RTC设置为24小时格式
    RTC_Handler.Init.AsynchPrediv = 0X7F;         //RTC异步分频系数(1~0X7F)
    RTC_Handler.Init.SynchPrediv = 0XFF;          //RTC同步分频系数(0~7FFF)
    RTC_Handler.Init.OutPut = RTC_OUTPUT_DISABLE;
    RTC_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    if (HAL_RTC_Init(&RTC_Handler) != HAL_OK)
        return RT_ERROR;

    if (HAL_RTCEx_BKUPRead(&RTC_Handler, RTC_BKP_DR0) != 0X5050) //是否第一次配置
    {
        // rt_kprintf("rtc is not configured\n");

        RTC_TimeStructure.Hours = 12;
        RTC_TimeStructure.Minutes = 12;
        RTC_TimeStructure.Seconds = 12;
        RTC_TimeStructure.TimeFormat = RTC_HOURFORMAT12_PM;
        RTC_TimeStructure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
        RTC_TimeStructure.StoreOperation = RTC_STOREOPERATION_RESET;
        if (HAL_RTC_SetTime(&RTC_Handler, &RTC_TimeStructure, RTC_FORMAT_BIN) != HAL_OK)
            return RT_ERROR;

        RTC_DateStructure.Date = 12;
        RTC_DateStructure.Month = 12;
        RTC_DateStructure.Year = 12;
        RTC_DateStructure.WeekDay = 12;
        if (HAL_RTC_SetDate(&RTC_Handler, &RTC_DateStructure, RTC_FORMAT_BIN) != HAL_OK)
            return RT_ERROR;

        HAL_RTCEx_BKUPWrite(&RTC_Handler, RTC_BKP_DR0, 0X5050); //标记已经初始化过了
    }

    /*not Allow access to RTC */
    HAL_PWR_DisableBkUpAccess();

    return RT_EOK;
}


int rt_hw_rtc_init(void)
{
    rtc.type = RT_Device_Class_RTC;

    if (RTC_Configuration() != RT_EOK)
    {
        // rt_kprintf("rtc configure fail...\r\n");
        return 0;
    }

    /* register rtc device */
    rtc.init    = RT_NULL;
    rtc.open    = rtc_open;
    rtc.close   = RT_NULL;
    rtc.read    = RT_NULL;
    rtc.write   = RT_NULL;
    rtc.control = rtc_control;

    /* no private */
    rtc.user_data = RT_NULL;

    rt_device_register(&rtc, "rtc", RT_DEVICE_FLAG_RDWR);

    return 0;
}

INIT_BOARD_EXPORT(rt_hw_rtc_init);

void get_boot_time_write_bootlog(void)
{
    int fd;
    time_t now;
    rt_uint8_t log_buf[40] = {0};
    struct tm *myTime;

    /* get current time */
    now = time(RT_NULL);
    rt_enter_critical();
    /* converts calendar time time into local time. */
    myTime = localtime(&now);
    rt_exit_critical();

    rt_sprintf((char *)log_buf, "boot time : %02d-%02d-%02d %02d:%02d:%02d week:%d\r\n",
               myTime->tm_year + 1900, myTime->tm_mon + 1, myTime->tm_mday,
               myTime->tm_hour, myTime->tm_min, myTime->tm_sec, myTime->tm_wday);

    fd = open("/boot.txt", DFS_O_RDWR | DFS_O_CREAT | DFS_O_APPEND, 0x777);
    write(fd, log_buf, 40);
    if (close(fd) != 0)
        rt_kprintf("log file close failed!\n");
}
